﻿using System;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Reflection;
using ibba.agil.core.Internal;
using ibba.agil.core.common;


namespace ibba.agil.core
{
    public abstract class CollectionManager<L, T>
        where L : List<T>
        where T : EntityData
    {



        public abstract L Where(CriteriaInfo.Criteria searchEngine);

        public abstract L Where(CriteriaInfo.Criteria searchEngine, TransactionManager tm);


        protected L Materializer(CriteriaInfo.Criteria searchEngine)
        {
            using (TransactionManager tm = new TransactionManager("Materializer"))
            {
                return Materializer(searchEngine, tm);
            }
        }

        protected L Materializer(CriteriaInfo.Criteria searchEngine, TransactionManager tm)
        {
            var DEFAULT_CORE_CUSTOM_MSG = "NAO FOI POSSIVEL CARREGAR DADOS DO SERVIDOR (1102).";
            Int16 cp = 2;
            var errorContext = string.Empty;
            var _where = new EntityWhere();
            var criteria = searchEngine.Invoke();
            T realEntity = (T)criteria.EntityData;
            try
            {
                errorContext = "Apply filter.. ";
                var _sql = string.Empty;
                if (searchEngine.Invoke().Count > 0)
                {
                    foreach (var engine in searchEngine.Invoke())
                    {
                        if (realEntity == null)
                        {
                            realEntity = (T)engine.Key.EntityData;
                        }

                        switch (engine.Key.SearchType)
                        {
                            case LogicalComparator.LIKE:
                                _where.Like(realEntity, engine.Key.LikePattern, engine.Value);
                                break;
                            case LogicalComparator.EQUAL:
                                var colIdentifier = realEntity.Mapper[engine.Key.NameProperty];
                                _where.Equal(engine.Key.LogicalType, colIdentifier, engine.Value);
                                break;
                            default:

                                break;
                        }
                    }
                    _sql = EntitySqlLanguage.Select(realEntity, _where);
                }
                else
                {
                    _sql = EntitySqlLanguage.Select(realEntity);
                }
                cp = 3;

                errorContext = "Query Command " + _sql + "\n";
                SqlCommand sqlCommand
                   = new SqlCommand(_sql, tm.Connection);
                errorContext = "Execute Command ";
                return Reeder(realEntity, ref cp, sqlCommand);
            }
            catch (Exception e)
            {
                tm.Dispose();
                throw new DASException("Materializer", cp, errorContext + "[" + e.Message + "]", DEFAULT_CORE_CUSTOM_MSG, e);
            }

        }


        private static L ReederFromCache(EntityData entity, ref Int16 cp, SqlCommand sqlCommand)
        {
            return null;
        }

        private static L Reeder(T entity, ref Int16 cp, SqlCommand sqlCommand)
        {
            cp = 2;
            SqlDataReader dataReeder = sqlCommand.ExecuteReader();
            cp = 3;
            var etityType = entity.GetType();
            var typeOfCollection = typeof(L);
            var contextError = string.Empty;
            L listaRetorno = (L)Activator.CreateInstance(typeOfCollection);
            cp = 3;
            try
            {
                while (dataReeder.Read())
                {
                    Type[] ctorParameter = Type.EmptyTypes;
                    ConstructorInfo con = etityType.GetConstructor(ctorParameter);
                    object entityMaterialized = con.Invoke(new object[] { });
                    cp = 4;
                    contextError = "Process begins to extract props";
                    foreach (var oProps in etityType.GetProperties())
                    {
                        contextError = String.Format("Err in {0} ~> {1}", oProps.Name, entity.Mapper[oProps.Name]);

                        var dataValueReeder = dataReeder[entity.Mapper[oProps.Name]];
                        switch (oProps.PropertyType.Name)
                        {
                            case "DateTime":
                                cp = 5;
                                var dateValue = dataValueReeder != DBNull.Value ? Convert.ToDateTime(dataValueReeder) : Convert.ToDateTime("01/01/0001 00:00:00");
                                dataValueReeder = dateValue;
                                break;

                            case "Int32":
                                var numberValue = dataValueReeder != DBNull.Value ? Convert.ToInt32(dataValueReeder) : 0;
                                dataValueReeder = numberValue;
                                break;

                            case "Int16":
                                var numberValue16 = dataValueReeder != DBNull.Value ? Convert.ToInt16(dataValueReeder) : 0;
                                dataValueReeder = numberValue16;
                                break;
                            case "Nullable`1":
                                var vlrNull = dataValueReeder != DBNull.Value ? Convert.ToDateTime(dataValueReeder) : Convert.ToDateTime("01/01/0001 00:00:00");
                                dataValueReeder = vlrNull;
                                break;
                            default:
                                break;
                        }

                        if (string.IsNullOrEmpty(dataValueReeder.ToString()))
                        { dataValueReeder = ""; }
                        contextError = String.Format("Try to set value on {0} with {1}",
                                                      oProps.Name,
                                                      dataValueReeder);
                        oProps.SetValue(entityMaterialized, dataValueReeder, null);
                    }
                    cp = 6;
                    var entityMaterializedT = (T)entityMaterialized;
                    listaRetorno.Add(entityMaterializedT);
                }
            }
            catch (Exception e)
            {
                throw new DASException("Reeder", cp, contextError, "Internal", e);
            }
            return listaRetorno;
        }
    }
}
